package panes

import "curses"
import "fmt"

type Rune int
type Style int32

type Pane interface {
    Put( col, line int, ch Rune, s Style ) Pane
    PutString( col, line int, chars string, s Style ) Pane
    Highlight( col, line int, count int, s Style ) Pane
    Size() (cols, rows int)    
}

func ClearLine( p Pane, line int, ch int ) {
    width, _ := p.Size()
    for i := 1; i <= width; i += 1 { p.Put( i, line, Rune(ch), 0 ) }
}

func SplitVertical(p Pane) (left, right Pane) {
    cols, rows := p.Size()
    halfCols := cols/2
    left = &SubPane{p, 0, 0, halfCols, rows}
    right = &SubPane{p, halfCols, 0, cols - halfCols - 1, rows}
    return
}

type FramedPane struct {Base Pane; marks map[int]bool; totalLines, topLine int}

func Frame(base Pane) *FramedPane {
    return &FramedPane{Base: base, marks: make(map[int]bool), totalLines: 0, topLine: 0}
}

func (f *FramedPane) SetVerticalScrolling( topLine, totalLines int ) *FramedPane {
    f.topLine, f.totalLines = topLine, totalLines
    return f
}

func (f *FramedPane) Put( col, line int, ch Rune, s Style ) Pane {
    f.Base.Put( col + 1, line + 1, ch, s )
    return f
}

func (f *FramedPane) PutString( col, line int, chars string, s Style ) Pane {
    f.Base.PutString( col + 1, line + 1, chars, s )
    return f
}

func (f *FramedPane) Size() (cols, rows int) {
    c, r := f.Base.Size()
    return c - 2, r - 2
}

func (f *FramedPane) Highlight( col, line int, count int, s Style ) Pane {
    width, _ := f.Size()
    f.Base.Highlight( col + 1, line + 1, min( width, count ), s )
    return f
}

func (f *FramedPane) DisplayFrame(comment string) {
    colour := Style(curses.Color_pair(3))
    base := f.Base
    cols, rows := base.Size()
//    
    for i := 1; i <= cols; i += 1 { base.Put( i, 1, curses.ACS_HLINE, colour ) }
    for i := 1; i <= cols; i += 1 { base.Put( i, rows, curses.ACS_HLINE, colour ) }
    for j := 1; j <= rows; j += 1 { base.Put( 1, j, curses.ACS_VLINE, colour ) }     
    for j := 1; j <= rows; j += 1 { base.Put( cols, j, curses.ACS_VLINE, colour ) }
//
    base.Put( 1, 1, curses.ACS_ULCORNER, colour )
    base.Put( cols, 1, curses.ACS_URCORNER, colour )
    base.Put( 1, rows, curses.ACS_LLCORNER, colour )
    base.Put( cols, rows, curses.ACS_LRCORNER, colour )
//
    for line, _ := range f.marks {
        base.Put( 1, line + 1, '>', Style(curses.Color_pair(0)) )
    }
//    
    lines := rows - 2
    if 0 < f.totalLines && lines <= f.totalLines {
        scrollTop, scrollBottom := 3, rows - 2
	    base.Put( cols, scrollTop, curses.ACS_BTEE, colour ) 
	    base.Put( cols, scrollBottom, curses.ACS_TTEE, colour )
	    for i := scrollTop + 1; i < scrollBottom; i += 1 { base.Put( cols, i, ' ', colour ) }
	//
	    regionSize := scrollBottom - scrollTop - 1
	    lumpSize := lines * regionSize / f.totalLines
        freePlay := regionSize - lumpSize
    //
        offset := (f.topLine - 1) * freePlay / f.totalLines
        anchor := scrollTop + offset	    
	//    
	    for i := 1; i < 1 + lumpSize - 1; i += 1 {
           base.Put( cols, i + anchor, curses.ACS_VLINE, colour )     
	    }
	    base.Put( cols, 1 + anchor, curses.ACS_TTEE, colour )
	    base.Put( cols, anchor + lumpSize - 1, curses.ACS_BTEE, colour )
	//    
	    if true {
	        f.PutString( 1, rows - 12, fmt.Sprintf( "anchor: %v ...", anchor ), colour )
		    f.PutString( 1, rows - 11, fmt.Sprintf( "height: %v ...", rows ), colour )
		    f.PutString( 1, rows - 10, fmt.Sprintf( "lump size: %v, free play: %v...", lumpSize, freePlay ), colour )
	        f.PutString( 1, rows - 9, fmt.Sprintf( "total lines: %v, top line: %v ...", f.totalLines, f.topLine ), colour )
	        f.PutString( 1, rows - 8, "A/B inc/dec total lines, C/D inc/dec topLine", colour )
	    }
	}
//
    if len(comment) > 0 {
	    const start = 3
	    base.Put( start, 1, curses.ACS_RTEE, colour )
	    base.Put( start + 1, 1, ' ', colour )
	    base.PutString( start + 2, 1, comment, colour )
	    base.Put( start + 2 + len(comment), 1, ' ', colour )
	}
}

func (f *FramedPane) SetMark( line int ) Pane {
    f.marks[line] = true
    return f
}

func (f *FramedPane) ClearMark( line int ) Pane {
    f.marks[line] = false, false
    return f
}

type WindowPane struct { w *curses.Window; colOff, lineOff, width, height int }

func NewWindowPane(cols, rows int) *WindowPane {
    return &WindowPane{ curses.Stdwin, -1, -1, cols, rows }
}

func (w *WindowPane) Put( col, line int, ch Rune, s Style ) Pane {
    if 0 <= col && col < w.width && 0 <= line && line <= w.height {
        w.w.Addch( col + w.colOff, line + w.lineOff, int32(ch), int32(s) )
    }
    return w
}

func (w *WindowPane) PutString( col, line int, chars string, s Style ) Pane {
    if 1 <= col && col <= w.width && 1 <= line && line <= w.height {
        w.w.Addstr( col + w.colOff, line + w.lineOff, chars, int32(s) )
    }
    return w
}

func (w *WindowPane) Size() (cols, rows int) {
    return w.width, w.height
}

func (w *WindowPane) Highlight( col, line int, count int, s Style ) Pane {
    w.w.ChAttr( col + w.colOff, line + w.lineOff, count, 0, int(s) )
    return w
}

type SubPane struct {p Pane; colOff, lineOff, width, height int } 

func (p *SubPane) Put( col, line int, ch Rune, s Style ) Pane {
    p.p.Put( col + p.colOff, line + p.lineOff, ch, s )
    return p
}

func (p *SubPane) PutString( col, line int, chars string, s Style ) Pane {
    p.p.PutString( col + p.colOff, line + p.lineOff, chars, s )
    return p
}

func (p *SubPane) Size() (cols, rows int) {
    return p.width, p.height
}

func min( x, y int ) int {
    if x < y { return x } else { return y }
    return 17 // BAH
}
func (p *SubPane) Highlight( col, line int, count int, s Style ) Pane {
    p.p.Highlight( col + p.colOff, line + p.lineOff, count, s )
    return p
}

